package com.chinatelecom.ifree.business.util;

import java.security.Key;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;

/**
 * 3DES 加解密工具类
 * 加密密钥 key 和加密向量 iv 可修改
 * @author Administrator
 *
 */
public class SecurityUtilFor3DES {

	//默认密钥，可以通过参数传递
	private static String SECURITY_ENCRYPTKEY = "FCB5023DA5545ED8680A2D07806DBDCA6ACDCF47E08F6A2D";
	//默认加密向量，可以通过参数传递 
	public static byte[] iv = {1,2,3,4,5,6,7,8};
	
	private static String m_strKeyArithmetic = "DESEDE";
	public static final String ENCODING_RAW = "RAW";
	public static final String ENCODING_BASE64 = "BASE64";

	private static final byte[] ENCODINGTABLE = { (byte) 'A', (byte) 'B',
			(byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
			(byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
			(byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q',
			(byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
			(byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
			(byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
			(byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
			(byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
			(byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
			(byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
			(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
			(byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
			(byte) '+', (byte) '/' };
	
	private static final byte[] decodingTable;

	static {
		decodingTable = new byte[128];

		for (int i = 'A'; i <= 'Z'; i++) {
			decodingTable[i] = (byte) (i - 'A');
		}

		for (int i = 'a'; i <= 'z'; i++) {
			decodingTable[i] = (byte) (i - 'a' + 26);
		}

		for (int i = '0'; i <= '9'; i++) {
			decodingTable[i] = (byte) (i - '0' + 52);
		}

		decodingTable['+'] = 62;
		decodingTable['/'] = 63;
	}

	/**
	 * 生成3DES密文
	 */
	public static String encryptTo3Des(String password,String keyStr,String ivStr) {

		String rtn = "";
		// 生成key
		java.security.Key key = null;
		byte[] encryptStr = null;

		try {
			if(keyStr!=null && !"".equals(keyStr.trim())) {
				//使用入参
				key=getKey(keyStr);
				SecurityUtilFor3DES.SECURITY_ENCRYPTKEY=keyStr;
			}
			else{
				//使用默认值
				key = getKey(SECURITY_ENCRYPTKEY);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			encryptStr = encode(encrypt(password, key,ivStr));
		} catch (Exception e) {
			e.printStackTrace();
		}
		rtn = new String(encryptStr);
		return rtn;
	}

	/**
	 * 解密3DES密文
	 * @throws Exception
	 */
	public static String decryptFrom3Des(String encryptStr,String keyStr,String ivStr){
		java.security.Key key = null;
		try {
			if(keyStr!=null && !"".equals(keyStr.trim())) {
				//使用入参
				key=getKey(keyStr);
				SecurityUtilFor3DES.SECURITY_ENCRYPTKEY=keyStr;
			}
			else{
				//使用默认值
				key = getKey(SECURITY_ENCRYPTKEY);
			}
			return new String(decrypt(decode(encryptStr), key,ivStr));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;

	}

	/**
	 * 通过key字符串获得Key
	 * @param keyString
	 * @return
	 * @throws Exception
	 */
	private static Key getKey(String keyString) throws Exception {
		return byte2Key(decode1(keyString));
	}

	/**
	 * 把一个字节数组转换成密钥对象
	 * @param keyBytes
	 * @return
	 * @throws Exception
	 */
	private static Key byte2Key(byte[] keyBytes) throws Exception {
		DESedeKeySpec spec = new DESedeKeySpec(keyBytes);
		SecretKeyFactory keyfactory = SecretKeyFactory
				.getInstance(m_strKeyArithmetic);
		return keyfactory.generateSecret(spec);
	}

	

	/**
	 * decode the base 64 encoded input data.
	 *
	 * @return a byte array representing the decoded data.
	 */
	public static byte[] decode1(byte[] data) {
		byte[] bytes;
		byte b1, b2, b3, b4;

		if (data[data.length - 2] == '=') {
			bytes = new byte[(((data.length / 4) - 1) * 3) + 1];
		} else if (data[data.length - 1] == '=') {
			bytes = new byte[(((data.length / 4) - 1) * 3) + 2];
		} else {
			bytes = new byte[((data.length / 4) * 3)];
		}

		for (int i = 0, j = 0; i < data.length - 4; i += 4, j += 3) {
			b1 = decodingTable[data[i]];
			b2 = decodingTable[data[i + 1]];
			b3 = decodingTable[data[i + 2]];
			b4 = decodingTable[data[i + 3]];
			bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
			bytes[j + 2] = (byte) ((b3 << 6) | b4);
		}

		if (data[data.length - 2] == '=') {
			b1 = decodingTable[data[data.length - 4]];
			b2 = decodingTable[data[data.length - 3]];
			bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
		} else if (data[data.length - 1] == '=') {
			b1 = decodingTable[data[data.length - 4]];
			b2 = decodingTable[data[data.length - 3]];
			b3 = decodingTable[data[data.length - 2]];
			bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
		} else {
			b1 = decodingTable[data[data.length - 4]];
			b2 = decodingTable[data[data.length - 3]];
			b3 = decodingTable[data[data.length - 2]];
			b4 = decodingTable[data[data.length - 1]];
			bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
			bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
		}

		return bytes;
	}

	/**
	 * decode the base 64 encoded String data.
	 *
	 * @return a byte array representing the decoded data.
	 */
	private static byte[] decode(String data) {
		byte[] bytes;
		byte b1, b2, b3, b4;

		if (data.charAt(data.length() - 2) == '=') {
			bytes = new byte[(((data.length() / 4) - 1) * 3) + 1];
		} else if (data.charAt(data.length() - 1) == '=') {
			bytes = new byte[(((data.length() / 4) - 1) * 3) + 2];
		} else {
			bytes = new byte[((data.length() / 4) * 3)];
		}

		for (int i = 0, j = 0; i < data.length() - 4; i += 4, j += 3) {
			b1 = decodingTable[data.charAt(i)];
			b2 = decodingTable[data.charAt(i + 1)];
			b3 = decodingTable[data.charAt(i + 2)];
			b4 = decodingTable[data.charAt(i + 3)];
			bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
			bytes[j + 2] = (byte) ((b3 << 6) | b4);
		}

		if (data.charAt(data.length() - 2) == '=') {
			b1 = decodingTable[data.charAt(data.length() - 4)];
			b2 = decodingTable[data.charAt(data.length() - 3)];
			bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
		} else if (data.charAt(data.length() - 1) == '=') {
			b1 = decodingTable[data.charAt(data.length() - 4)];
			b2 = decodingTable[data.charAt(data.length() - 3)];
			b3 = decodingTable[data.charAt(data.length() - 2)];
			bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
		} else {
			b1 = decodingTable[data.charAt(data.length() - 4)];
			b2 = decodingTable[data.charAt(data.length() - 3)];
			b3 = decodingTable[data.charAt(data.length() - 2)];
			b4 = decodingTable[data.charAt(data.length() - 1)];
			bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
			bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
		}

		return bytes;
	}

	private static byte[] decode1(String hex) {
		StringBuffer buffer = new StringBuffer(hex.length());
		char c;
		for (int i = 0; i < hex.length(); i++) {
			if (!Character.isWhitespace(c = hex.charAt(i))) {
				if (!Character.isLetterOrDigit(c)) {
					throw new IllegalArgumentException("Illegal hex char");
				}
				buffer.append(c);
			}
		}
		if (buffer.length() % 2 != 0) {
			throw new IllegalArgumentException("Bad padding");
		}
		byte[] result = new byte[buffer.length() / 2];
		for (int i = 0; i < result.length; i++) {
			try {
				result[i] = (byte) Integer.parseInt(buffer.substring(2 * i,
						2 * i + 2), 16);
			} catch (Exception e) {
				throw new IllegalArgumentException("Illegal hex char");
			}
		}
		return result;
	}

	private static byte[] encode(byte[] data) {
		byte[] bytes;
		int modulus = data.length % 3;
		if (modulus == 0) {
			bytes = new byte[4 * data.length / 3];
		} else {
			bytes = new byte[4 * ((data.length / 3) + 1)];
		}
		int dataLength = (data.length - modulus);
		int a1, a2, a3;
		for (int i = 0, j = 0; i < dataLength; i += 3, j += 4) {
			a1 = data[i] & 0xff;
			a2 = data[i + 1] & 0xff;
			a3 = data[i + 2] & 0xff;
			bytes[j] = ENCODINGTABLE[(a1 >>> 2) & 0x3f];
			bytes[j + 1] = ENCODINGTABLE[((a1 << 4) | (a2 >>> 4)) & 0x3f];
			bytes[j + 2] = ENCODINGTABLE[((a2 << 2) | (a3 >>> 6)) & 0x3f];
			bytes[j + 3] = ENCODINGTABLE[a3 & 0x3f];
		}
		/*
		 * process the tail end.
		 */
		int b1, b2, b3;
		int d1, d2;
		switch (modulus) {
		case 0: /* nothing left to do */
			break;
		case 1:
			d1 = data[data.length - 1] & 0xff;
			b1 = (d1 >>> 2) & 0x3f;
			b2 = (d1 << 4) & 0x3f;
			bytes[bytes.length - 4] = ENCODINGTABLE[b1];
			bytes[bytes.length - 3] = ENCODINGTABLE[b2];
			bytes[bytes.length - 2] = (byte) '=';
			bytes[bytes.length - 1] = (byte) '=';
			break;
		case 2:
			d1 = data[data.length - 2] & 0xff;
			d2 = data[data.length - 1] & 0xff;
			b1 = (d1 >>> 2) & 0x3f;
			b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
			b3 = (d2 << 2) & 0x3f;
			bytes[bytes.length - 4] = ENCODINGTABLE[b1];
			bytes[bytes.length - 3] = ENCODINGTABLE[b2];
			bytes[bytes.length - 2] = ENCODINGTABLE[b3];
			bytes[bytes.length - 1] = (byte) '=';
			break;
		}
		return bytes;
	}

	private static byte[] decrypt(byte[] byteIn, Key key,String ivStr) throws Exception {
		//使用入参的 加密向量
		if(ivStr!=null && !"".equals(ivStr.trim()))
		{
			//加密向量 以 "," 分割
        	String[] defaultIVStr=ivStr.split(",");
        	if(defaultIVStr.length==8)
        	{
        		for(int i=0;i<8;i++)
        		{
//        			System.out.println(Integer.parseInt(defaultIVStr[i].trim()));
        			iv[i]=(byte) Integer.parseInt(defaultIVStr[i].trim());
        		}
        	}
		}
		return decrypt(iv, byteIn, key, "desede", ENCODING_RAW);
	}

	public static byte[] decrypt(byte[] keyIV, byte[] bIn, byte[] seed,
			String strKeyArithmetic, String strArithmetic, String encoding)
			throws Exception {
		SecureRandom sr = new SecureRandom(seed);
		KeyGenerator kGen = KeyGenerator.getInstance(strKeyArithmetic);

		if (strArithmetic.equalsIgnoreCase("desede")) {
			kGen.init(168, sr);
		} else {
			kGen.init(sr);
		}

		Key key = kGen.generateKey();

		return decrypt(keyIV, bIn, key, strArithmetic, encoding);
	}

	private static byte[] decrypt(byte[] keyIV, byte[] bIn, Key key,
			String strArithmetic, String encoding) throws Exception {
		Cipher cipher = Cipher.getInstance(strArithmetic + "/CBC/PKCS5Padding");
		IvParameterSpec ips = (keyIV.length == 0) ? new IvParameterSpec(iv)
				: new IvParameterSpec(keyIV);

		cipher.init(Cipher.DECRYPT_MODE, key, ips);

		byte[] bOut = cipher.doFinal(bIn);

		if (encoding.equalsIgnoreCase(ENCODING_BASE64)) {
			bOut = encodeBase64(bOut);
		}

		return bOut;
	}

	private static byte[] encodeBase64(byte[] bytes) {
		return encode(bytes);
	}

	private static byte[] encrypt(String strIn, Key key,String ivStr) throws Exception {
		Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
		//使用入参的 加密向量
		if(ivStr!=null && !"".equals(ivStr.trim()))
		{
			//加密向量 以 "," 分割
        	String[] defaultIVStr=ivStr.split(",");
        	if(defaultIVStr.length==8)
        	{
        		for(int i=0;i<8;i++)
        		{
//        			System.out.println(Integer.parseInt(defaultIVStr[i].trim()));
        			iv[i]=(byte) Integer.parseInt(defaultIVStr[i].trim());
        		}
        	}
		}
		IvParameterSpec ips = new IvParameterSpec(iv);
		cipher.init(Cipher.ENCRYPT_MODE, key, ips);
		byte[] bOut = cipher.doFinal(strIn.getBytes("UTF-8"));
		return bOut;
	}
	
	
	public static void main(String[] aegs)
	{
		
		String password="abcd123456";
	    
	    //使用默认密钥和加密向量
	    String encryptStr = SecurityUtilFor3DES.encryptTo3Des(password,null, null);
	    System.out.println(encryptStr);
	    String srcpwd = SecurityUtilFor3DES.decryptFrom3Des(encryptStr,null, null);
	    System.out.println(srcpwd);
	    
	    //只使用默认的加密向量
	    encryptStr = SecurityUtilFor3DES.encryptTo3Des(password,"FCB5023DA5545ED8680A2D07806DBDCA6ACDCF47E08F6A2F",null);
	    System.out.println(encryptStr);
	    srcpwd = SecurityUtilFor3DES.decryptFrom3Des(encryptStr,"FCB5023DA5545ED8680A2D07806DBDCA6ACDCF47E08F6A2F",null);
	    System.out.println(srcpwd);
	    
	    encryptStr = SecurityUtilFor3DES.encryptTo3Des(password,"98FF2F8908F36EC2563EF9A56635F38898FF2F8908F36EC2", "8,7,6,5,1,2,3,4");
	    System.out.println(encryptStr);
	    encryptStr="4xApSS+fQ5k=";
	    srcpwd = SecurityUtilFor3DES.decryptFrom3Des(encryptStr,"FCB5023DA5545ED8680A2D07806DBDCA6ACDCF47E08F6A2D", "1,2,3,4,5,6,7,8");
	    System.out.println(srcpwd);
		
	}

}
